package sipphone.example;

import publish.AVReceive2;
import publish.AVTransmit2;
import publish.Receiver;
import publish.Sender;

import javax.media.*;
import javax.media.rtp.InvalidSessionAddressException;
import javax.media.rtp.SessionAddress;
import javax.sdp.*;
import javax.sip.*;
import javax.sip.address.SipURI;
import javax.sip.header.*;
import javax.sip.message.Message;
import javax.sip.message.Request;
import javax.sip.message.Response;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Vector;

/**
 * Created by RXJ on 2018/2/1.
 */
public class InviteHandler extends SipCall implements ResponseListener {
    int localMediaPort;

    String registerHost;
    int registerPort;
    String localHost;
    int localPort;

    SipFactory sipFactory;
    boolean initialized;

    public InviteHandler(String registerHost, int registerPort,
                         String localHost, int localPort) {
        this.registerHost = registerHost;
        this.registerPort = registerPort;
        this.localHost = localHost;
        this.localPort = localPort;

        sipHandler = SipHandler.getInstance(localHost, localPort);
        localMediaPort = -1;
        try {
            DatagramSocket socket = new DatagramSocket();
            localMediaPort = socket.getLocalPort();
            System.out.println("监听接口:" + localMediaPort);
            socket.disconnect();
            socket.close();
           // initRTP(localMediaPort);
        } catch (Exception e1) {
            System.out.println(e1);
        }
        sipHandler.addResponseListener(this);
        sipFactory = SipFactory.getInstance();
        initialized = true;
    }

    /**
     * @throws ParseException
     * @throws PeerUnavailableException
     * @throws InvalidArgumentException
     * @throws TransactionUnavailableException
     * @throws SipException
     */
    void makeCall(String recipient) throws ParseException,
            PeerUnavailableException, InvalidArgumentException,
            TransactionUnavailableException, SipException {
        if (initialized) {
            sipHandler.setCallID(System.currentTimeMillis() + "@" + sipHandler.getLocalHost());
            this.recipient = recipient;
            sendInitialInvite(recipient);
        }

    }

    /**
     * @param recipient
     * @throws ParseException
     * @throws PeerUnavailableException
     * @throws InvalidArgumentException
     * @throws TransactionUnavailableException
     * @throws SipException
     */
    private void sendInitialInvite(String recipient) throws ParseException,
            PeerUnavailableException, InvalidArgumentException,
            TransactionUnavailableException, SipException, ParseException {
        System.out.println("Calling " + recipient + " at " + registerHost);

        ContentTypeHeader contentTypeHeader = null;
        //content type should be application/sdp (not applications)
        //reported by Oleg Shevchenko (Miratech)
        contentTypeHeader = sipFactory.createHeaderFactory()
                .createContentTypeHeader("application", "sdp");

        String sdpContent = "v=0\n" + "o=" + sipHandler.getUsername() + " 0 0 IN IP4 "
                + SipHandler.getInstance().getLocalHost() + "\n" + "s=-\n"
                + "c=IN IP4 " + SipHandler.getInstance().getLocalHost() + "\n"
                + "t=0 0\n" + "m=audio " + localMediaPort
                + " RTP/AVP 4 3 0 5 6 8 15 18\n" + "a=sendrecv\n"
                + "a=rtpmap:101 telephone-event/8000 \n"
                + "a=fmtp:101 64\n"
                + "a=rtpmap:0 PCMU/8000\n";
        System.out.println("sdpContent =" + sdpContent);

        sipHandler.sendRequest(recipient, Request.INVITE, sdpContent, contentTypeHeader);

    }

    public static void main(String[] agrs) {
        int pid = 0;
        RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
        String name = runtime.getName();
        System.out.println("当前进程的标识为：" + name);
        int index = name.indexOf("@");
        if (index != -1) {
            pid = Integer.parseInt(name.substring(0, index));
            System.out.println("当前进程的PID为：" + pid);
        }
        try {
            int port = pid;
            String ip = InetAddress.getLocalHost().getHostAddress();
            System.out.println("--port = " + port + "\n--ip = " + ip);

            InviteHandler inviteHandler =
                    new InviteHandler("192.168.1.218",
                            5060, ip, port);

            inviteHandler.makeCall("sip:13250333247@192.168.1.218");


        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void handleResponse(ResponseEvent responseEvent) {
        System.out.println("handleResponse");
        Response response = responseEvent.getResponse();
        try {
            sendAck(response, responseEvent);
            startConversation(response, null);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建 rtp接收者
     *
     * @param port
     */
    private void startAVReceive2(int port) {
        String[] array = new String[1];
        array[0] = String.valueOf(registerHost+"/"+port);
        AVReceive2 avReceive2 = new AVReceive2(array,localMediaPort);
        if (!avReceive2.initialize()) {
            System.err.println("Failed to initialize the sessions.");
            System.exit(-1);
        }
        try {
            while (!avReceive2.isDone())
                Thread.sleep(1000);
        } catch (Exception e) {
            System.out.println(e);
        }

        System.err.println("Exiting AVReceive2");
    }



    private void sendAck(Response response, ResponseEvent responseEvent) {
        Dialog dialog = responseEvent.getDialog();
        try {
          /*
            Request ack = dialog.createRequest(Request.ACK);
            dialog.sendAck(ack);
           */
            SipURI requestURI = sipFactory.createAddressFactory().createSipURI(
                    sipHandler.getUsername(), sipHandler.getRegistrarHost());

            ArrayList<ViaHeader> viaHeaders = new ArrayList<>();
            //创建 viaHeaders 根据 response 中获取
            ViaHeader viaHeader = (ViaHeader) response.getHeader("Via");

            viaHeaders.add(viaHeader);

            CallIdHeader callIdHeader = (CallIdHeader) response.getHeader("Call-ID");

            FromHeader fromHeader = (FromHeader) response.getHeader("From");

            ToHeader toHeader = (ToHeader) response.getHeader("To");

            CSeqHeader cSeqHeader = sipFactory.createHeaderFactory()
                    .createCSeqHeader(sipHandler.sequenceNum++, Request.ACK);

            //创建  Max_forwards 默认：70
            MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory()
                    .createMaxForwardsHeader(70);


            Request ackRequest = sipFactory.createMessageFactory()
                    .createRequest(
                            requestURI,
                            Request.ACK,
                            callIdHeader,
                            cSeqHeader,
                            fromHeader,
                            toHeader,
                            viaHeaders,
                            maxForwards
                    );
            sipHandler.sendRequest(ackRequest);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @param response
     * @param clientTransaction
     * @throws SipException
     * @throws SdpParseException
     * @throws SdpException
     * @throws IOException
     * @throws NoDataSourceException
     * @throws NoProcessorException
     * @throws InterruptedException
     * @throws NotConfiguredError
     * @throws NotRealizedError
     * @throws NoDataSinkException
     * @throws InvalidSessionAddressException
     * @throws UnknownHostException
     */
    void startConversation(Message response,
                           Transaction clientTransaction) throws SipException,
            SdpParseException, SdpException, IOException,
            NoDataSourceException, NoProcessorException, InterruptedException,
            NotConfiguredError, NotRealizedError, NoDataSinkException,
            InvalidSessionAddressException {
        System.out.println("Starting conversation");

        String sdpData = new String(response.getRawContent());
        SdpFactory sdpFactory = SdpFactory.getInstance();
        SessionDescription sessionDescription = sdpFactory
                .createSessionDescription(sdpData);
        Vector mediaDescriptions = sessionDescription
                .getMediaDescriptions(true);
        System.out.println("We have " + mediaDescriptions.size() + " media descriptions");
        for (int mdNum = 0; mdNum < mediaDescriptions.size(); mdNum++) {
            MediaDescription mediaDescription = (MediaDescription) mediaDescriptions
                    .elementAt(mdNum);
            Media media = mediaDescription.getMedia();
            String proto = media.getProtocol();
            String type = media.getMediaType();
            int port = media.getMediaPort();

            Vector formats = media.getMediaFormats(true);

            if (formats.size() < 1) {
                System.out.println(
                        "In SIP outbound call: No audio formats");
            }
            int sdpFormat = SdpConstants.PCMU;
            try {
                sdpFormat = Integer.parseInt((String) formats.elementAt(0));
            } catch (NumberFormatException nfExc) {
                nfExc.printStackTrace();
            }

            transmitter = new RtpTransmitter(registerHost, port, sdpFormat);

           startAVReceive2(port);
           startAVTransmit2(port);
          //  startReceiver(registerHost, port);
            System.out.println("Starting transmitter");
            transmitter = new RtpTransmitter(registerHost, port, sdpFormat);

        }
    }

    private void startAVTransmit2(int port) {

        //设置本地地址和远端地址都为本机地址，自己传给自己，试验用
        try {
       /*     SessionAddress local = new SessionAddress(
                    InetAddress.getLocalHost(),localMediaPort);
            SessionAddress target = new SessionAddress(
                    InetAddress.getByName(registerHost),port);
            SessionAddress target = new SessionAddress(
                    InetAddress.getLocalHost(),localMediaPort);*/
      //      new Sender(local,target);
          //  new Receiver(local,target);
        String[] args = new String[]{"javasound://44100",
                 ""+registerHost, ""+port,""+localMediaPort};
         int i=0;
         Format fmt = null;
         AVTransmit2 at = new AVTransmit2(new MediaLocator(args[i]),
                    args[i + 1], args[i + 2],args[i+3], fmt);
            // Start the transmission
            String result = at.start();

            // result will be non-null if there was an error. The return
            // value is a String describing the possible error. Print it.
            if (result != null) {
                System.err.println("Error : " + result);
                System.exit(0);
            }

            System.err.println("Start transmission for 60 seconds...");

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
    private void startReceiver(String host, int port)
            throws InvalidSessionAddressException, IOException,
            UnknownHostException {
        System.out.println(
                "Starting conversation on " + localMediaPort + " to " + host + ":" + port);
        SessionAddress remoteAddress = new SessionAddress(InetAddress
                .getByName(host), port);
        rtpManager.addTarget(remoteAddress);
    }
}
